export {};
/*
  装饰器函数
  我们要在一个类或方法上使用装饰器，首先需要提供一个装饰器函数
  这个函数会在该装饰器被使用的时候调用

 使用装饰器
  在需要被装饰的类或方法前通过 @装饰器名称 来调用装饰器
  @f
  class 类名{}
  装饰器可以类加，可以一行也可以多行书写

  类装饰器
  类装饰器应用于构造函数，可以用来监视、修改或替换类定义
  类的构造函数会作为类装饰器函数的唯一一个参数
  function f(constructor:Function){}

注意
  类装饰器不能用在声明文件中( .d.ts)，也不能用在任何外部上下文中（比如declare的类）。
*/

/** 1. 装饰器执行时机*/
//类装饰器↓是应用于类的构造函数
function Age<T extends {new(...args:any[]):{}}>(constructor:T):T{
  console.log(1);
  class Person2 extends constructor{
    age: number = 0;
  }

  /** 如果类装饰器返回一个值，那么会使用这个返回的值替换被装饰的类的声明*/
  return Person2;
}

//Age是一个装饰器函数，该函数会自动调用，不需要()调用，调用的时候会传入下面这个对应的class的构造函数
@Age
class Person {
  username = 'Kimoo';

  //如果@Age拿到Person以后 要在Person身上或则Person.prototype上声明一些属性
  //需要在Person{}这里面进行声明 比如 go!:Function
}

// 这里就会打印 1 ，也就是执行Age装饰器函数

let p1 = new Person();
console.log(p1); //Person2 {username: "Kimoo", age: 0}




/** 2. 装饰器工厂*/
// 想要自定义年龄
// 如果我们希望传入构造值，那么就得使用: 闭包
/*
function fn(e,a,b){
  return a+b+e.clientX;
}
document.onclick = fn;
document.onclick = (e)=>{fn(e,10,29)});
*/
//想要实现这种功能，Age就不能直接作为装饰器函数

//↓该函数执行完成后需要返回一个函数，这个返回的函数将作为实际的装饰器函数
function Age2(v:number){
  //↓这个才是真正装饰器要执行的函数
  return function <T extends {new(...args:any[]):{}}>(constructor:T):T{
    console.log(1);
    class Person2 extends constructor{
      age: number = v;
    }

    return Person2;
  }
}

@Age2(20)
class Cat1 {
  username = '小猫咪';
}





//其它例子

/*function logClass(params:any){
  console.log(params); //params就是当前类

  params.prototype.apiUrl = 'xxx';

  params.prototype.run = function(){
    console.log('我是一个run方法');
  }
}*/


/** 没有返回值 就是还是返回原类*/
function logClass(params:string){
  return function(target:any){
    console.log(target);
    console.log(params);

    target.prototype.apiUrl = params;
  }
}

@logClass('http://www.abc.com/api')
class HttpClient{
  constructor(){

  }

  getData(){}
}

const http = new HttpClient();
console.log('http.apiUrl:', http.apiUrl);

console.log(new Cat1()); //Person2 {username: "小猫咪", age: 20}

